#!/usr/bin/env python
'''
	
	
	
	
	
	Credit where credit is due: some ideas gleaned from
	https://code.google.com/p/appengine-rest-server/
	Copyright (c) 2008 Boomi, Inc. (under an apache license)
'''
import cgi
import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
#for django 1.2
from google.appengine.dist import use_library
use_library('django', '1.2')

from django.utils import simplejson as json

#rss parser feedparser.org 
import feedparser



from google.appengine.api import urlfetch
from google.appengine.api import users

from google.appengine.ext import db
from google.appengine.ext.db import djangoforms

from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.ext.webapp import util
from google.appengine.ext.webapp.util import run_wsgi_app

BASE_CT = "application/vnd.plasmo-com.rss"
JSON_CT = BASE_CT + "+json"
#a fall back to the generic json, used by default in jquery.
JSON_CT_D = "application/json"
#can't use the default application/xml, it is used in http requests by webkit. 
XML_CT = BASE_CT + "+xml"
HTML_CONTENT_TYPE = "text/html"

def get_request_mime_type(request):
	return request.accept.best_match([JSON_CT, XML_CT, JSON_CT_D, HTML_CONTENT_TYPE])

class Feed(db.Model):
	name = db.StringProperty()
	'''placeholder="Default will read from feed"'''
	desc = db.StringProperty(multiline=True)
	''',placeholder="Default will read from feed"'''
	url = db.StringProperty()
	'''placeholder="http://whatever/rss.xml"'''
	added = db.UserProperty()	
	date = db.DateTimeProperty(auto_now_add=True)

class FeedForm(djangoforms.ModelForm):
	class Meta:
		model = Feed
		exclude = ["added"]
		
class Feeds(webapp.RequestHandler):
	def get(self):
		#method for showing the list 
		user =  users.get_current_user()
		if user is None: 
			self.redirect(users.create_login_url(self.request.uri))
			return
		
		if users.get_current_user():
			url = users.create_logout_url(self.request.uri)
			url_linktext = 'Logout'
		#else: we're not showing if not logged in. 
		#	url = users.create_login_url(self.request.uri)
		#	url_linktext = 'Login'
		feeds_query = Feed.all().order('-name')
		#feeds = feeds_query.fetch()
		greeting = 'Hello, '+user.nickname()
		template_values = {
			'greeting': greeting,
			'feeds' : feeds_query,
			'feedForm': FeedForm(),
			'url' : url,
			'url_linktext': url_linktext,
		}
		path = os.path.join(os.path.dirname(__file__), 'settings.html')
		self.response.out.write(template.render(path, template_values))

	def post(self):

		resp = dict(status='failure')
		user =  users.get_current_user()
		if not user: 
			resp['message'] = 'not logged in'
		else:
			data = ItemForm(data=self.request.POST)
			if data.is_valid():
				# Save the data, and redirect to the view page
				feed = data.save(commit=False)
				try:
					result = urlfetch.fetch(feed.url)
					if result.status_code == 200:
						d = feedparser.parse(result.content)
					else:
						self.response.out.write('{status:"failure",message:"Problem downloading from url provided, status:'+result.status_code+'"}')
				except urlfetch.DownloadError, e: 
					self.response.out.write('{status:"failure",message:"Problem downloading from url provided"}')
					
				#for field in d.feed:
				#	self.response.out.write(field + "\n")
				try:
					if feed.name == "":
						feed.name = d.feed.title
					if feed.desc == "":
						feed.desc = d.feed.description
					feed.added = user
					feed.put()
					self.response.http_status_message(201)
					self.response.headers['Location'] = "/feeds/"+feed.key()
				except KeyError:
					resp['message'] = "invalid feed specified, check the url"
				except AttributeError:
					resp['message'] = "invalid feed specified, check the url"
			else:
				# Reprint the form
				resp['message'] = "make sure you enter a url, I\'m watchin you"
			
			if resp['status'] == 'failure':
				out_mime_type = get_request_mime_type(self.request)
				self.response.headers['Content-Type'] = out_mime_type
				if out_mime_type == JSON_CT or out_mime_type == JSON_CT_D:
					#If we request json or our special json, do that
					self.response.out.write(json.dumps(resp))
				elif out_mime_type == XML_CT:
					#If we request our xml, do that. Generic XML won't work as some browers 
					#prioritise it over html, and we can't have that. 
					self.response.out.write("would have dumped xml")
				else:
					#Do the default, where we return HTML
					path = os.path.join(os.path.dirname(__file__), 'error.html')
					self.response.out.write(template.render(path, resp))
			else:
				#we have set a http status message for success, so do nothing. 
				pass

class FeedCheck(webapp.RequestHandler):
	def get(self):				
		resp = dict(status='failure')
		user =  users.get_current_user()
		if not user: 
			resp['message'] = 'not logged in'
		else:
			url = self.request.get('url')
			if url is not None:
				# Save the data, and redirect to the view page
				try:
					result = urlfetch.fetch(url)
					if result.status_code == 200:
						d = feedparser.parse(result.content)
						resp['status'] = 'success'
						resp['message'] = 'Name: '+d.feed.title+"\n Description: "+d.feed.description
						resp['name'] = d.feed.title
						resp['desc'] = d.feed.description
					else:
						resp['message'] = "Problem downloading from url provided"
				except urlfetch.InvalidURLError:
					resp['message'] = "URL was not valid"
				except urlfetch.DownloadError, e: 
					resp['message'] = "Problem downloading from url provided"
			else:
				resp['message'] = "make sure you enter a url, I\'m watchin you"

		out_mime_type = get_request_mime_type(self.request)
		self.response.headers['Content-Type'] = out_mime_type
		if out_mime_type == JSON_CT or out_mime_type == JSON_CT_D:
			#If we request json or our special json, do that
			self.response.out.write(json.dumps(resp))
		elif out_mime_type == XML_CT:
			#If we request our xml, do that. Generic XML won't work as some browers 
			#prioritise it over html, and we can't have that. 
			self.response.out.write("would have dumped xml")
		else:
			#Do the default, where we return HTML
			path = os.path.join(os.path.dirname(__file__), 'error.html')
			self.response.out.write(template.render(path, resp))
	
class FeedPosts(webapp.RequestHandler):
	def get(self):		
		out_mime_type = get_request_mime_type(self.request)

		user =  users.get_current_user()
		if not user: 
			self.response.out.write('{status:"failure",message:"not logged in"}')
			exit()

		d = None
		url = self.request.get('url')
		if url is not None:
			# Save the data, and redirect to the view page
			try:
				result = urlfetch.fetch(url)
				if result.status_code == 200:
					d = feedparser.parse(result.content)
					#self.response.out.write('{status:"success",name:"'+d.feed.title+'",desc:"'+d.feed.description+'"}')
					#title author published link 
					#might need to format the date first. 
					entries = []
					for entry in d.entries:
						self.validateEntry(entry)
						entries.append(dict(title=entry.title,desc=entry.description,link=entry.link,author=entry.author, published=entry.published ))
					
					resp = dict(status='success',name=d.feed.title,desc=d.feed.description,feed=entries)
					self.response.out.write(json.dumps(resp))
				else:
					self.response.out.write('{status:"failure",message:"Problem downloading from url provided, status:'+result.status_code+'"}')
			except urlfetch.InvalidURLError:
				self.response.out.write('{status:"failure",message:"URL was not valid"}')
			except urlfetch.DownloadError, e: 
				self.response.out.write('{status:"failure",message:"Problem downloading from url provided"}')
		else:
			# Reprint the form
			self.response.out.write('{status:"failure",message:"no url, internal error"}')
			
	#see also getattr(obj,attr,default_value)
	def validateEntry(self,entry):
		fields = ["title","description","author","link","published"]
		for f in fields:
			if not hasattr(entry,f):
				setattr(entry,f,'')

class Generator(webapp.RequestHandler):
	def get(self):
		pass
		
class MainHandler(webapp.RequestHandler):
	def get(self):
		user =  users.get_current_user()
		if user:
			url = users.create_logout_url(self.request.uri)
			url_linktext = 'Logout'
		else:
			self.redirect(users.create_login_url(self.request.uri))
			return

		self.response.headers['Content-Type'] = 'text/html'
		greeting = 'Hello, '+user.nickname()

		feeds = Feed.all().order('-name')
		template_values = {
			'greeting' : greeting,
			'feeds' : feeds,
			'url' : url,
			'url_linktext': url_linktext,
		}
		path = os.path.join(os.path.dirname(__file__), 'index.html')
		self.response.out.write(template.render(path, template_values))

'''
	We're updating this to make it a RESTFul interface. 
	/ - home page, the actual functionality plas wanted. 
	/feeds - access to the list of feeds, by: 
		GET + ACCEPT: 
		PUT to update
		POST to store (return 201 Created + Location)
		DELETE to delete 
		
		301 moved permanently 
		302 moved temporarily 
		
		400 bad request
		401 unauthorized 
		403 forbidden
		404 not found, unknown whether it might be found in future. 
		405 method not allowed - must return an Allow header with list of valid methods. 
		406 not acceptable: ie client accepts XML but service cannot create XML. 
			Caveat: Services should return alternate formats regardless. ? 
				Note: HTTP/1.1 servers are allowed to return responses which are
			    not acceptable according to the accept headers sent in the
			    request. In some cases, this may even be preferable to sending a
			    406 response. User agents are encouraged to inspect the headers of
			    an incoming response to determine if it is acceptable.
		Use HTTP status codes; ie 409 Conflict. 
		410 gone - Link is Intentionally removed and the owners request that this link be removed permanently. 
'''

class Director(webapp.RequestHandler):
	def get(self):
		if self.request.path == "/settings":
			#self.response.http_status_message(301)
			'''
	        self.response.headers["Content-Type"] = "image/png"
	        self.response.headers.add_header("Expires", "Thu, 01 Dec 1994 16:00:00 GMT")
			'''
			#True uses a 301 redirect. 
			self.redirect("/feeds",True)
		elif self.request.path == "/posts":
			self.redirect("/feeds/posts",True)
		else:
			self.response.http_status_message(404)
			
class DebugChecker(webapp.RequestHandler):
	def get(self):
		self.response.out.write(self.request.accept)
		self.response.out.write("\n<br/>CheckSet: ")
		self.request.headers['accept'] = 'application/vnd.plasmo-com.rss+json;version=1.0'
		self.response.out.write(self.request.accept)
		self.response.out.write("\n<br/>Acceptable: ")		
		for t in [JSON_CT, XML_CT, JSON_CT_D, HTML_CONTENT_TYPE]:
			self.response.out.write("\n<br/> "+t)		
		self.response.out.write("\n<br/>CheckFound: ")		
		self.response.out.write(self.request.accept.best_match([JSON_CT, XML_CT, JSON_CT_D, HTML_CONTENT_TYPE]))
		
def main():
	application = webapp.WSGIApplication([('/', MainHandler),
											('/feeds', Feeds), 
											('/feeds/posts',FeedPosts),
											('/generate', Generator), 
											('/check',FeedCheck),
											('/posts',FeedPosts),
											('/settings', Director),
											('/debug',DebugChecker)],
										 debug=True)
	util.run_wsgi_app(application)


if __name__ == '__main__':
	main()
